home *** CD-ROM | disk | FTP | other *** search
-
- #import "NewsGroupSetBrowser.h"
- #import "Newsgroup.h"
- #import "NewsgroupBrowserCell.h"
- #import <misckit/MiscAppDefaults.h>
- #import "MatrixSet.h"
- #import "MatrixScroller.h"
- #import "instr.h"
- #import "regexpr.h"
-
- @implementation NewsGroupSetBrowser
-
- #define IS_LEAF 1
- #define IS_NO_LEAF 2
-
- - awakeFromNib
- {
- buttonTitle=NXCopyStringBuffer([selectionButton title]);
- [self setCellPrototype:[[[NewsgroupBrowserCell alloc] init] setNewsgroupCell:nil]];
- return self;
- }
-
- - free
- {
- if(myVisibleCellList!=nil)
- [myVisibleCellList free];
- free(buttonTitle);
- return [super free];
- }
-
- - initFrame:(const NXRect *)frameRect
- {
- int minWidth;
-
- [super initFrame:frameRect];
-
- minWidth=[NXApp defaultIntValue:"MinBrowserColumnWidth"];
- if(minWidth==0)
- minWidth=70;
-
- [self setAction:@selector(selectNewsgroup:)];
- [self setTarget:mySet];
- [self setPathSeparator:'.'];
- [self setMultipleSelectionEnabled:NO];
- [self setDelegate:self];
- [self setEmptySelectionEnabled:YES];
- [self setHorizontalScrollButtonsEnabled:NO];
- [self setHorizontalScrollerEnabled:YES];
- [self setMinColumnWidth:minWidth];
- [self setTitled:NO];
- [self reuseColumns:YES];
- [self separateColumns:YES];
- [self useScrollBars:YES];
- [self useScrollButtons:YES];
- listList=[[List alloc] init];
-
- return self;
- }
-
- - (int)browser:sender fillMatrix:matrix inColumn:(int)column
- {
- struct bcellDesc{
- int leaf;
- id newsgroup;
- };
- int i,y,z,from,ii;
- List *aList,*bList,*cList;
- Newsgroup *aNewsgroup;
- const char *name;
- char *end_of_name;
- char *cell_name;
- NXHashState hashState;
- HashTable *aHashTable;
- char *aStringKey;
- char buf[255];
- char *path;
- char isnull[]="";
- struct bcellDesc *c;
-
- //compute column selection list
- if(column>0){
- //get selected path
- path=[self getPath:buf toColumn:column];
- NX_ASSERT(path!=NULL,"No path selected");
- path++;
-
- y=[listList count];
- for(i=y-1;i>=column;i--)
- [[listList removeObjectAt:i] free];
- bList=[[List alloc] init];
- aList=[listList objectAt:column-1];
- z=[aList count];
- for(i=0;i<z;i++){
- const char *lname;
- int plen=strlen(path);
- aNewsgroup=[aList objectAt:i];
- lname=[aNewsgroup stringValue];
- if((strncmp(lname,path,plen)==0) && (lname[plen]=='.'))
- [bList addObject:aNewsgroup];
- }
- [listList addObject:bList];
- }
- else
- path=isnull;
-
- //compute column entries
- aList=[listList objectAt:column];
- aHashTable=[[HashTable alloc] initKeyDesc:"*" valueDesc:"!"];
- z=[aList count];
- for(i=0;i<z;i++){
- int isLeaf;
- aNewsgroup=[aList objectAt:i];
- name=[aNewsgroup stringValue];
- name+=strlen(path);
- if(path[0]!='\0')
- name++;
-
- end_of_name=strchr(name,'.');
- if(end_of_name==NULL){
- cell_name=NXCopyStringBuffer(name);
- isLeaf=IS_LEAF;
- }
- else{
- int size=strlen(name)-strlen(end_of_name);
- cell_name=(char *)malloc((size+3)*sizeof(char));
- strncpy(cell_name,name,size);
- cell_name[size]='\0';
- isLeaf=IS_NO_LEAF;
- }
- if([aHashTable isKey:(void *)cell_name]){
- c=(struct bcellDesc *)[aHashTable valueForKey:(void *)cell_name];
- free(cell_name);
- }
- else{
- c=(struct bcellDesc *)calloc(1,sizeof(struct bcellDesc));
- [aHashTable insertKey:(const void *)cell_name value:(void *)c];
- }
- c->leaf|=isLeaf;
- if(isLeaf==IS_LEAF)
- c->newsgroup=aNewsgroup;
- }
-
- //column enties are now in the hashtable
- //now create column matrix
- hashState=[aHashTable initState];
- i=-1;
- while([aHashTable nextState:&hashState key:(const void **)&aStringKey value:(void **)&c]){
- id aCell;
- [matrix addRow];
- i++;
- aCell=[matrix cellAt:i :0];
- [aCell setLeaf:((c->leaf&IS_LEAF)==IS_LEAF)];
- [aCell setStringValueNoCopy:aStringKey shouldFree:YES];
- if(c->newsgroup!=nil)
- [aCell setNewsgroupCell:c->newsgroup];
- if(c->leaf==3){
- [matrix addRow];
- i++;
- aCell=[matrix cellAt:i :0];
- [aCell setLeaf:NO];
- [aCell setStringValue:aStringKey];
- }
- free(c);
- }
- i++;
-
- //simple sorting :-(
- cList=[matrix cellList];
- for(from=0;from<i-1;from++){
- int min=from;
- id minObject=[cList objectAt:from];
- const char *minString=[minObject stringValue];
- for(ii=from+1;ii<i;ii++){
- int c;
- id anObject=[cList objectAt:ii];
- const char *aString=[anObject stringValue];
- if( ((c=strcmp(aString,minString))<0) ||
- (c==0 && [anObject isLeaf]) ){
- minObject=anObject;
- minString=aString;
- min=ii;
- }
- }
- if(min!=from){
- id anObject=[cList replaceObjectAt:from with:[cList objectAt:min]];
- [cList replaceObjectAt:min with:anObject];
- }
- }
-
- [aHashTable free];
- return i;
- }
-
- - loadMatrix
- {
- [self makeVisibleCellList:self];
- [self loadColumnZero];
-
- return self;
- }
-
- - reloadMatrix
- {
- char path_buf[255];
- id curSel;
-
- [[self window] disableFlushWindow];
- [self setAutodisplay:NO];
-
- curSel=[mySet currentSelection];
-
- [self getPath:path_buf toColumn:[self lastColumn]];
-
- [self loadMatrix];
-
- //reselect path or path+cell
- if(curSel==nil){
- if(path_buf[0]!='\0'){
- int i,j,z;
- z=strlen(path_buf)-1;
- j=[myVisibleCellList count];
- //see if there's a visible group with its prefix==path_buf
- for(i=0;i<j;i++)
- if(strncmp([[myVisibleCellList objectAt:i] stringValue],path_buf+1,z)==0){
- strcat(path_buf,".");
- [self setPath:path_buf];
- break;
- }
- }
- }
- else if([curSel isTaged]==TRUE){
- const char *gname=[curSel stringValue];
- char *newPath;
-
- newPath=(char *)malloc((strlen(gname)+2)*sizeof(char));
- sprintf(newPath,".%s",gname);
- [self setPath:newPath];
- free(newPath);
- }
-
- [[self setAutodisplay:YES] display];
- [[[self window] reenableFlushWindow] flushWindow];
- return self;
- }
-
- - setMatrixCellList:(List *)aList
- {
- myCellList=aList;
- return self;
- }
-
-
- - (List *)cellList
- {
- return myVisibleCellList;
- }
-
- - removeInvalidCell:aCell;
- {
- return [self removeInvalidCell:aCell andUpdate:YES];
- }
-
- - removeInvalidCell:aCell andUpdate:(BOOL)update
- {
- int i,j;
- for(i=0,j=[listList count];i<j;i++)
- [[listList objectAt:i] removeObject:aCell];
- [myVisibleCellList removeObject:aCell];
- [[myCellList removeObject:aCell] free];
-
- if(update==YES)
- [self update];
-
- return self;
- }
-
- - update
- {
- [self loadColumnZero];
- [self display];
- [mySet sync];
- [NXApp updateWindows];
-
- return self;
- }
-
- - (List *)getCurrSelections
- {
- List *aList=[[List alloc] init];
- id selection;
-
- selection=[self currSelection];
- if(selection!=nil)
- [aList addObject:selection];
- else {
- int j=[listList count];
- NX_ASSERT(j>0,"Listlist empty");
- if(j>1)
- if([[listList objectAt:j-1] count]>1) //shit!
- [aList appendList:[listList objectAt:j-1]];
- }
- return aList;
- }
-
-
- - currSelection
- {
- char buf[255];
- char *newsg_name;
- id aCell=[self selectedCell];
- id selCell=nil;
- List *aList;
- int i,j;
-
- if(aCell==nil)
- return nil;
- if([aCell isLeaf]==TRUE){
- const char *cellName=[aCell stringValue];
- int lcol=[self lastColumn];
- [self getPath:buf toColumn:lcol];
-
- newsg_name=(char *)malloc((strlen(buf)+strlen(cellName)+3)*sizeof(char));
- newsg_name[0]='\0';
- strcpy(newsg_name,buf);
- strcat(newsg_name,".");
- strcat(newsg_name,cellName);
-
- aList=[listList objectAt:lcol];
- j=[aList count];
- for(i=0;i<j;i++){
- if(strcmp([[aList objectAt:i] stringValue],newsg_name+1)==0){
- selCell=[aList objectAt:i];
- break;
- }
- }
- free(newsg_name);
- }
- return selCell;
- }
-
- - makeVisibleCellList:sender
- {
- int i,j;
-
- if(myVisibleCellList!=nil)
- [myVisibleCellList free];
- myVisibleCellList=[[List alloc] init];
- j=[myCellList count];
- for(i=0;i<j;i++){
- id aCell=[myCellList objectAt:i];
- if([aCell isTaged]==TRUE)
- [myVisibleCellList addObject:aCell];
- }
-
- if([listList count]==0)
- [listList addObject:myVisibleCellList];
- else
- [listList replaceObjectAt:0 with:myVisibleCellList];
-
- return self;
- }
-
- - (id)selectionButton
- {
- return selectionButton;
- }
-
- - setButtonTitle:(const char *)title
- {
- free(buttonTitle);
- [selectionButton setTitle:title];
- buttonTitle=NXCopyStringBuffer(title);
-
- return self;
- }
-
- - _delayedUpdate:sender
- {
- [selectionButton setTitle:buttonTitle];
- return self;
- }
-
- - updateButtonTitle
- {
- [self perform:@selector(_delayedUpdate:) with:self afterDelay:0.0 cancelPrevious:TRUE];
- return self;
- }
-
- - (BOOL)selectNextCell:(int)delta inColumn:(int)col leafSelected:(BOOL *)sel
- {
- id matrix=[self matrixInColumn:col];
- id aCell=[matrix selectedCell];
- id cellList=[matrix cellList];
- int j,jj;
- int i=0;
- char buf[255];
- id newCell;
-
- [matrix getNumRows:&j numCols:&jj];
-
- *sel=FALSE;
- if(j==0)
- return FALSE;
- //find index of cell to select
- if(aCell==nil){
- if(delta>0)
- i=0;
- else
- i=j-1;
- }
- else{
- int pos=[cellList indexOf:aCell];
- if(((delta>0)&&(pos==j-1))||((delta<0)&&(pos==0)))
- return FALSE;
- i=pos+delta;
- }
-
- //compose and set path
- [self getPath:buf toColumn:col];
- newCell=[cellList objectAt:i];
- if(![newCell isLeaf]){
- strcat(buf,".");
- strcat(buf,[newCell stringValue]);
- strcat(buf,".");
- [self setPath:buf];
- }
- else{
- strcat(buf,".");
- strcat(buf,[newCell stringValue]);
- [self setPath:buf];
- *sel=TRUE;
- }
-
- return TRUE;
- }
-
- - (BOOL)selectNextCell:(int)delta
- {
- int i;
- BOOL r,rr;
-
- NX_ASSERT(((delta==1) || (delta==-1)),"Wrong parameter selectNextCell");
-
- for(i=[self lastColumn];i>=0;i--){
- r=[self selectNextCell:delta inColumn:i leafSelected:&rr];
- if(rr==TRUE){
- [self sendAction];
- return TRUE;
- }
- if(r==TRUE)
- i=[self lastColumn]+1;
- }
-
- return FALSE;
- }
-
- - setPath:(const char *)aPath
- {
- char *newPath,*c;
- id bmatrix;
- int i,j;
-
- NX_ASSERT(*aPath==(char)pathSeparator,"setPath: wrong pathstring");
-
- if(aPath[strlen(aPath)-1]==(char)pathSeparator){
- if([self selectedCell]!=nil)
- [[self matrixInColumn:[self lastColumn]] selectCellAt:-1 :-1];
- return [super setPath:aPath];
- }
-
- newPath=NXCopyStringBuffer(aPath);
-
- c=strrchr(newPath,pathSeparator);
- NX_ASSERT(c!=NULL,"setPath: internal error");
-
- *c='\0';
- if(*newPath=='\0')
- [super setPath:"."];
- else if([super setPath:newPath]==nil)
- return nil;
- c++;
-
- bmatrix=[self matrixInColumn:[self lastColumn]];
- [bmatrix getNumRows:&j numCols:&i];
- for(i=0;i<j;i++){
- id aCell=[bmatrix cellAt:i :0];
- if(!strcmp([aCell stringValue],c))
- if([aCell isLeaf]){
- [bmatrix selectCellAt:i :0];
- break;
- }
- }
- free(newPath);
- [self scrollColumnToVisible:[self lastColumn]];
- [self scrollSelectionToVisible];
-
- if(i==j)
- return nil;
- return self;
- }
-
- - scrollSelectionToVisible
- {
- id aMatrix=[self matrixInColumn:[self lastVisibleColumn]];
- id aCell=[aMatrix selectedCell];
- if(aCell!=nil)
- [aMatrix scrollCellToVisible:[[aMatrix cellList] indexOf:aCell]
- upperOffset:1.5 lowerOffset:1.5];
-
- return self;
- }
- //-------------------------
- // searchable text protocol
- //-------------------------
-
- - (oneway void)makeSelectionVisible
- {
- }
-
- - (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
- {
- return SEARCH_CANNOT_WRITE;
- }
-
- - (oneway void)replaceSelection:(const char *)replacement
- {
- }
-
- - (const char *)stringValueForCellAt:(int)index
- {
- return [[myVisibleCellList objectAt:index] stringValue];
- }
-
- - (int)searchFor:(const char *)pattern mode:(SearchMode)mode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(out int *)pos size:(out int *)size
- {
- int ccount;
- int s_pos;
- int fStart,fEnd;
- int delta;
- List *selList;
-
- unsigned char fm[256], tr[256];
- struct re_pattern_buffer rpat;
-
- s_pos=-2;
- fStart=fEnd=-2;
-
- ccount=[myVisibleCellList count];
- if(ccount==0)
- return 0;
-
- // find first selected cell
- selList=[self getCurrSelections];
- if([selList count]>0)
- s_pos=[myVisibleCellList indexOf:[selList objectAt:0]];
- [selList free];
-
- if(!rev){
- if(s_pos<0){
- fStart=0; fEnd=ccount;
- }
- else{
- fStart=s_pos+1; fEnd=s_pos+ccount;
- }
- }
- else{
- if(s_pos<0){
- fStart=ccount-1; fEnd=-1;
- }
- else{
- fStart=s_pos-1+ccount; fEnd=s_pos+1;
- }
- }
-
- delta= rev? -1:1;
-
- if(regexpr){
- char *str;
- int i;
-
- memset(&rpat, 0, sizeof(rpat));
- for(i=256; i--;)
- tr[i] = i;
- if(!cases)
- for(i='A'; i<='Z'; i++)
- tr[i] = i-'A'+'a';
- rpat.translate = tr;
- rpat.fastmap = fm;
- str = re_compile_pattern((char *)pattern,strlen(pattern), &rpat);
- if (str!=NULL)
- return (strcmp(str, "Out of memory")?SEARCH_INVALID_REGEXPR:SEARCH_INTERNAL_ERROR);
- }
-
- for(;fStart!=fEnd;fStart+=delta){
- int index=fStart%ccount;
- const char *result=NULL;
- const char *cellString=[self stringValueForCellAt:index];
-
- if(regexpr){
- int l=strlen(cellString);
- int p=re_search_pattern(&rpat,(char *)cellString,l,0,l,0);
-
- if(p==-2)
- return SEARCH_INTERNAL_ERROR;
- result= (p==-1)? NULL : cellString;
- }
- else
- result=instr(cellString,pattern,cases);
-
- if(result!=NULL){
- *pos=index;
- *size=1;
- return 1;
- }
- }
-
- return 0;
- }
-
- - (oneway void)selectTextFrom:(int)start to:(int)end
- {
- if(start<=end){
- if(start==end){
- [super setPath:"."];
- [self sendAction];
- }
- else{
- if(start<[myVisibleCellList count]){
- id aCell=[myVisibleCellList objectAt:start];
- char *newPath=malloc((strlen([aCell stringValue])+2)*sizeof(char));
- sprintf(newPath,".%s",[aCell stringValue]);
- [self setPath:newPath];
- free(newPath);
- [self sendAction];
- }
- }
- }
- }
-
- - (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
- {
- id aCell=[self currSelection];
-
- if(aCell!=nil){
- const char *sval=[aCell stringValue];
- [pboard declareTypes:&type num:1 owner:NULL];
- [pboard writeType:type data:sval length:strlen(sval)];
- }
- return;
- }
-
- - mySet
- {
- return mySet;
- }
-
- - (BOOL)selectNewsgroupNamed:(const char *)gname
- {
- int i,j;
- for(i=0,j=[myVisibleCellList count];i<j;i++){
- const char *name=[[myVisibleCellList objectAt:i] stringValue];
- if(name && !strcmp(gname,name)){
- char *newPath=malloc((strlen(name)+2)*sizeof(char));
- sprintf(newPath,".%s",name);
- [self setPath:newPath];
- [self scrollSelectionToVisible];
- free(newPath);
- [self sendAction];
-
- return TRUE;
- }
- }
- return FALSE;
- }
-
- @end
-